//
// Ishtar C++ Utility Library
//
// Task Facility - Work Implementation
//

#include <Ishtar/Pch.h>

#include <Ishtar/Task/WorkImpl.h>
#include <Ishtar/Task/WorkExecutorImpl.h>


namespace Ishtar
{

namespace Task
{

///////////////////////////////////////////////////////////////////////////////
//
// Work
//

ISHTAR_REFERENCE_COUNTED_DEFINE( WorkImpl )


WorkImpl::WorkImpl( const std::string& name, const WorkFunction& function )
    : m_name( name )
    , m_function( function )
    , m_executor( NULL )
{}


void WorkImpl::Start( WorkExecutorImpl* executor )
{
    State::TransitResult tr;

    tr = m_state.TransitByEvent< WorkEventOrder >();
    if ( !tr )
    {
        ISHTAR_TRACE_FAIL( "Work state is not UNSTARTED, start failed" );
        ISHTAR_TRACE_INFO( "  name: %s, state: %u", m_name, tr.value );
        ISHTAR_INVALID_OPERATION();
    }

    ISHTAR_ASSERT( NULL == m_executor );
    m_executor = executor;
    m_executor->IncrementNumWorks();

    // Try to scheduling ....

    tr = m_state.TransitByEvent< WorkEventQueue >();

    m_executor->AddReadyWork( WorkImplPtr( this ));

    tr = m_state.TransitByEvent< WorkEventWait >();  // QUEUING -> READY
    if ( !tr )
    {
        if ( WORK_STATE_TO_CANCEL == tr.value )
        {   
            this->DoCancel();
            return;
        }
        else if ( WORK_STATE_READY < tr.value )
        {
            return;
        }

        ISHTAR_TRACE_WARN_HERE( "Not a valid state after add to executor" );
        ISHTAR_TRACE_INFO( "  name: %s, state: %u", m_name, tr.value );
        return;
    }
}


void WorkImpl::Run()
{
    if ( WORK_STATE_CANCELED == m_state ) { return; }

    State::TransitResult tr;
    tr = m_state.TransitByEvent< WorkEventRun >();  // READY -> RUNNING
    if ( !tr )
    {
        switch ( tr.value )
        {
        case WORK_STATE_CANCELING:
        case WORK_STATE_CANCELED:
            return;
        }

        ISHTAR_TRACE_WARN_HERE( "Not a valid state to RUNNING" );
        ISHTAR_TRACE_INFO( "  name: %s, state: %u", m_name, tr.value );
        return;
    }

    ExceptionCatcher< void > xc( m_function );
    if ( xc )
    {
        ISHTAR_TRACE_WARN_HERE( "Work throws" );
        ISHTAR_TRACE_INFO( "  name: %s", m_name );
    }
    
    tr = m_state.TransitByEvent< WorkEventEnd >();
    if ( !tr )
    {
        ISHTAR_TRACE_WARN_HERE( "Not a valid state to RUNNED" );
        ISHTAR_TRACE_INFO( "  name: %s, state: %u", m_name, tr.value );
        return;
    }

    m_executor->DecrementNumWorks();
}


Bool WorkImpl::Cancel()
{
    State::TransitResult tr;
    tr = m_state.TransitByEvent< WorkEventCancel >();

    if ( tr )
    {
        switch ( tr.value )
        {
        case WORK_STATE_TO_CANCEL:
            return true;  // this work would be canceled by the settling thread.
        
        case WORK_STATE_CANCELING:
            this->DoCancel();
            return true;
        
        default: ISHTAR_NOT_REACHED();
        }
    }
    else
    {
        if ( WORK_STATE_UNSTARTED == tr.value )
        {
            ISHTAR_TRACE_WARN_HERE( "Can't cancel an unstarted work" );
            ISHTAR_TRACE_INFO( "  name: %s", m_name );
        }

        // other states can't be canceled, or is already canceled.

        return false;
    }
}


void WorkImpl::DoCancel()
{
    ISHTAR_ASSERT( WORK_STATE_CANCELING == m_state );

    m_state.TransitByEvent< WorkEventEnd >();
    
    m_executor->DecrementNumWorks();
}


//
// Bridging
//

Work::Work()
{}


Work::Work( const std::string& name, const WorkFunction& function )
    : m_impl( new WorkImpl( name, function ))
{}


Work::Work( const WorkImplPtr& impl )
    : m_impl( impl )
{}


std::string Work::Name() const
{
    WorkImplPtr impl = m_impl.Share();
    return impl ? impl->Name() : std::string();
}


Bool Work::IsEmpty() const
{
    return !m_impl;
}


Bool Work::Cancel()
{
    WorkImplPtr impl = m_impl.Share();
    return impl ? impl->Cancel() : false;
}


Work Work::Exchange( const Work& rhs )
{
    WorkImplPtr temp = rhs.m_impl.Share();
    return Work( m_impl.Exchange( temp ));
}


Uint Work::GetInstanceCount()
{
    return WorkImpl::GetInstanceCount();
}


///////////////////////////////////////////////////////////////////////////////


} // namespace Task

} // namespace Ishtar
